unit uDLTCPDPDataExchange;

interface

uses
  SysUtils, Classes, Variants, WinSock,DLSocketComponent, DLSocketServer,
  DLTCPDPServer, DLSocketClient, DLSocketHandler, DLSocketHandlerV1,
  DLTCPClient, DLUDP, uDZSysUtils, uDZDataExchange;

type
  TDLTCPDPDataExchangeServer = class(TDzInterfacedObject, IDzDataExchangeServer)
  private
    fTCPServer: TDLTCPDPServer;
    fEventHandler: IDzDataExchangeEventHandler;
  private
    procedure OnAccept(listener: TDLTCPDPServer;
      connection: TDLTCPDPServerConnection);
    procedure OnData(listener: TDLTCPDPServer;
      connection: TDLTCPDPServerConnection);
    procedure OnDisconnect(listener: TDLTCPDPServer;
      connection: TDLTCPDPServerConnection);
  protected
    function GetActive: Boolean;
    procedure SetActive(value: Boolean);
    function GetEventHandler: IDzDataExchangeEventHandler;
    procedure SetEventHandler(const value: IDzDataExchangeEventHandler);
    function GetAttribute(const name: TNameString): Variant; override;
    procedure SetAttribute(const name: TNameString;
      const value: Variant); override;
  public
    constructor Create;
    destructor Destroy; override;
  end;

  TDLTCPDPDataExchange = class(TDzInterfacedObject, IDzDataExchangeTunnel)
  private
    fPeerIP: AnsiString;
    fPeerPort: Integer;
    fGuid: AnsiString;
    fConnection: TDLTCPDPServerConnection;
    function HandleException(e: Exception): Boolean;
  protected
    function Write(buf: Pointer; len: Integer): Integer;
    function Read(buf: Pointer; len: Integer): Integer;
    function WriteStream(stream: TStream; len: Integer): Integer;
    function ReadStream(stream: TStream; len: Integer): Integer;
    function Skip(len: Integer): Integer;
    procedure Open;
    procedure Close;
    function GetConnected: Boolean;
    procedure SetConnected(const Value: Boolean);
    function GetGuid: AnsiString;
    procedure SetGuid(const value: AnsiString);
    function GetAttribute(const name: TNameString): Variant; override;
    procedure SetAttribute(const name: TNameString;
      const value: Variant); override;
  public
    constructor Create(conn: TDLTCPDPServerConnection);
  end;

implementation

{ TDLTCPDPDataExchangeServer }

constructor TDLTCPDPDataExchangeServer.Create;
begin
  fTCPServer := TDLTCPDPServer.Create(nil);
  fTCPServer.OnConnect  := Self.OnAccept;
  fTCPServer.OnData := Self.OnData;
  fTCPServer.OnDisconnect := Self.OnDisconnect;
end;

destructor TDLTCPDPDataExchangeServer.Destroy;
begin
  fTCPServer.Free;                                                                               
  inherited;
end;

function TDLTCPDPDataExchangeServer.GetActive: Boolean;
begin
  Result := fTCPServer.Active;
end;

function TDLTCPDPDataExchangeServer.GetAttribute(
  const name: TNameString): Variant;
begin
  if (name = 'Port') then
    Result := fTCPServer.Port;
end;

function TDLTCPDPDataExchangeServer.GetEventHandler: IDzDataExchangeEventHandler;
begin
  Result := fEventHandler;
end;

procedure TDLTCPDPDataExchangeServer.OnAccept(listener: TDLTCPDPServer;
  connection: TDLTCPDPServerConnection);
begin
  connection.WaitDataTimeOut := 100000;
  fEventHandler.OnConnected(Self, TDLTCPDPDataExchange.Create(connection));
end;

procedure TDLTCPDPDataExchangeServer.OnData(listener: TDLTCPDPServer;
  connection: TDLTCPDPServerConnection);
begin
  fEventHandler.OnData(Self, IDzDataExchangeTunnel(connection.Data));
end;

procedure TDLTCPDPDataExchangeServer.OnDisconnect(listener: TDLTCPDPServer;
  connection: TDLTCPDPServerConnection);
var
  exchange: IDzDataExchangeTunnel;
begin
  exchange := IDzDataExchangeTunnel(connection.Data);
  fEventHandler.OnDisconnected(Self, exchange);
  connection.Data := nil;
  exchange._Release;
end;

procedure TDLTCPDPDataExchangeServer.SetActive(value: Boolean);
begin
  fTCPServer.Active := True;
end;

procedure TDLTCPDPDataExchangeServer.SetAttribute(const name: TNameString;
  const value: Variant);
begin
  inherited;
  if (name = 'Port') then
    fTCPServer.Port := value;
end;

procedure TDLTCPDPDataExchangeServer.SetEventHandler(
  const value: IDzDataExchangeEventHandler);
begin
  fEventHandler := value;  
end;

{ TDLTCPDPDataExchange }

procedure TDLTCPDPDataExchange.Close;
begin
  fConnection.Close;
end;

constructor TDLTCPDPDataExchange.Create(conn: TDLTCPDPServerConnection);
var
  exchange: IDzDataExchangeTunnel;
  ia: TInAddr;
begin
  exchange := Self;
  exchange._AddRef;
  conn.Data := Pointer(exchange);
  fConnection := conn;
  ia.S_addr := htonl(fConnection.PeerAddr);
  fPeerIP := StrPas(inet_ntoa(ia));
  fPeerPort := fConnection.PeerPort;
  fGuid := Format('%s:%d',[fPeerIP, fPeerPort]);  
end;

function TDLTCPDPDataExchange.GetAttribute(const name: TNameString): Variant;
begin
  if (name = 'RemoteIP') then Result := fPeerIP
  else if (name = 'RemotePort') then Result := fPeerPort
  else Result := Null;
end;

function TDLTCPDPDataExchange.GetConnected: Boolean;
begin
  Result := fConnection.Handle <> Cardinal(INVALID_SOCKET);
end;

function TDLTCPDPDataExchange.GetGuid: AnsiString;
begin
  Result := fGuid;
end;

function TDLTCPDPDataExchange.HandleException(e: Exception): Boolean;
begin
  if e is EDLSocketNormal then
  begin
    Result := False;
    Exit;
  end;

  if e is EDLSocketEntity then
  begin
    case EDLSocketEntity(e).ErrorCode of
      WSAECONNABORTED, WSAECONNRESET: Close;
    end;
    Result := True;
    Exit;
  end;

  if e is EDLSocketClosed then
  begin
    Close;
    Result := True;
    Exit;
  end;

  if e is EDLSocketSelectTimeout then
  begin
    Result := True;
    Exit;
  end;
  
  Result := False;
end;

procedure TDLTCPDPDataExchange.Open;
begin

end;

function TDLTCPDPDataExchange.Read(buf: Pointer; len: Integer): Integer;
begin
  try
    Result := fConnection.Recv(buf, len, 3000);
  except
    on e: Exception do
    begin
      if (HandleException(e)) then Result := 0
      else raise;
    end;
  end;
end;

function TDLTCPDPDataExchange.ReadStream(stream: TStream;
  len: Integer): Integer;
var
  buf: array [0..1023] of AnsiChar;
  buflen, bytesRead: Integer;
begin
  Result := 0;
  while (GetConnected and (Result < len)) do
  begin
    buflen := SizeOf(buf);
    if (buflen > len - Result) then buflen := len - Result;
    bytesRead := Self.Read(@buf, buflen);
    if (bytesRead > 0) then
    begin
      Inc(Result, bytesRead);
      stream.Write(buf, bytesRead);
    end;
  end;
end;

procedure TDLTCPDPDataExchange.SetAttribute(const name: TNameString;
  const value: Variant);
begin

end;

procedure TDLTCPDPDataExchange.SetConnected(const Value: Boolean);
begin
  if not Value then fConnection.Close;
end;

procedure TDLTCPDPDataExchange.SetGuid(const value: AnsiString);
begin

end;

function TDLTCPDPDataExchange.Skip(len: Integer): Integer;
var
  buf: array [0..1023] of AnsiChar;
  bytesRead: Integer;
begin
  Result := 0;
  while (GetConnected and (Result < len)) do
  begin
    if (len - Result > SizeOf(buf)) then bytesRead := SizeOf(buf)
    else bytesRead := len - Result;
    bytesRead := Self.Read(@buf, bytesRead);
    Inc(Result, bytesRead);
  end;
end;

function TDLTCPDPDataExchange.Write(buf: Pointer; len: Integer): Integer;
begin
  try
    fConnection.Send(buf, len);
    Result := len;
  except
    on e: Exception do
    begin
      Result := 0;
      if not HandleException(e) then raise;
    end;
  end;
end;

function TDLTCPDPDataExchange.WriteStream(stream: TStream;
  len: Integer): Integer;
var
  buf: array [0..1023] of AnsiChar;
  bytesRead, bytesWrite, remain: Integer;
begin
  if len > stream.Size - stream.Position then
    len := stream.Size - stream.Position;
  if stream is TMemoryStream then
  with TMemoryStream(stream) do
  begin
    Result := Self.Write(Pointer(LongInt(Memory) + Position), len);
  end
  else begin
    remain := len;
    while GetConnected and (remain > 0) do
    begin
      if remain < SizeOf(buf) then bytesRead := remain
      else bytesRead := SizeOf(buf);
      bytesRead := stream.Read(buf, bytesRead);
      bytesWrite := Self.Write(@buf, bytesRead);
      if bytesWrite = 0 then Break;
      stream.Seek(bytesWrite - bytesRead, soFromCurrent);
      Dec(remain, bytesWrite);
    end;
    Result := len - remain;
  end;
end;

end.
